Popular Searches
Popular Course Categories
Popular Courses

Storing and retrieving application data

Storing and retrieving application data

Firebase with Flutter

Storing and Retrieving Application Data in Flutter

Storing and retrieving application data is an essential part of Flutter application development. Most real-world applications need to save information such as user profiles, products, orders, messages, tasks, settings, and other application data.

Flutter applications can work with different types of storage depending on the requirements. For cloud-based application data, Cloud Firestore is a popular Firebase solution. Firestore is a NoSQL document database that stores data in collections and documents and supports one-time reads, queries, real-time listeners, and offline capabilities.


1. What is Application Data?

Application data is information created, entered, or used by an application during its operation.

Examples include:

  • User names and email addresses
  • User profiles
  • Products and categories
  • Orders and payments
  • Messages and comments
  • Tasks and notes
  • Course information
  • Application settings
  • Favorites and bookmarks

For example, an e-commerce application may store products like this:

products
├── product001
│   ├── name: "Laptop"
│   ├── price: 65000
│   └── category: "Electronics"
└── product002
    ├── name: "Phone"
    ├── price: 35000
    └── category: "Electronics"

2. Why Do Applications Need Data Storage?

Without persistent storage, information entered by users may disappear when the application is closed or the application state is recreated.

Data storage allows applications to:

  • Save user information.
  • Retrieve previously stored information.
  • Update existing information.
  • Delete information when required.
  • Synchronize information between devices.
  • Provide personalized experiences.
  • Maintain application state and records.

3. Types of Data Storage in Flutter

Different applications require different storage solutions.

Storage Type Common Use
Shared Preferences Small key-value settings and preferences
SQLite Structured local relational data
Hive Local NoSQL-style storage
Secure Storage Sensitive local values such as tokens
Cloud Firestore Cloud-based NoSQL application data
Firebase Realtime Database Real-time JSON-based application data

This topic focuses primarily on storing and retrieving application data using Cloud Firestore with Flutter.


4. What is Cloud Firestore?

Cloud Firestore is a flexible, scalable NoSQL document database provided by Firebase. It stores data in documents organized into collections. Documents can contain fields, nested objects, and subcollections.

Firestore also supports queries, real-time listeners, and offline capabilities on supported platforms.

Cloud Firestore Documentation


5. Firestore Data Model

Firestore uses a collection and document structure rather than traditional SQL tables and rows.

Firestore
   |
   +-- Collection
        |
        +-- Document
             |
             +-- Fields
             |
             +-- Subcollections

Example

users
 |
 +-- user001
      |
      +-- name: "John Doe"
      +-- email: "[email protected]"
      +-- age: 25
      +-- isActive: true

6. Collections

A collection is a group of related documents.

Examples:

users
products
orders
messages
courses
tasks

A collection can contain many documents.


7. Documents

A document is a record containing fields and values.

users
 |
 +-- user001
      |
      +-- name: "John Doe"
      +-- email: "[email protected]"
      +-- age: 25

Every document has an identifier. Firestore can generate an ID automatically, or your application can specify one.


8. Fields

Fields contain the actual data stored inside a document.

Field Example Type
name John Doe String
age 25 Number
isActive true Boolean
skills ["Flutter","Dart"] Array
address {city: "Mumbai"} Map
createdAt Timestamp Timestamp

9. Prerequisites

Before storing and retrieving application data with Cloud Firestore, you should have:

  • Flutter SDK installed.
  • A Flutter project.
  • A Firebase project.
  • Firebase configured with the Flutter application.
  • Cloud Firestore enabled in Firebase Console.

For Firebase configuration, follow the official Flutter setup documentation.

Firebase Setup for Flutter


10. Add Cloud Firestore to Flutter

Add the Cloud Firestore Flutter plugin using:

flutter pub add cloud_firestore

Then import it into the Dart file:

import 'package:cloud_firestore/cloud_firestore.dart';

11. Access Firestore

The Firestore instance can be accessed using FirebaseFirestore.instance.

final db = FirebaseFirestore.instance;

You can then use the instance to access collections and documents.


12. Firebase Initialization

Firebase must be initialized before Firestore operations are performed.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();

  runApp(const MyApp());
}

In a FlutterFire-configured application, Firebase is commonly initialized using generated configuration options.

await Firebase.initializeApp(
  options: DefaultFirebaseOptions.currentPlatform,
);

13. Storing Application Data

Storing data means writing information from the Flutter application into a database.

Firestore provides methods such as:

  • add()
  • set()
  • update()

14. Store Data with add()

The add() method creates a new document with an automatically generated document ID.

await FirebaseFirestore.instance
    .collection('users')
    .add({
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 25,
});

Firestore generates a unique document ID for the new document.


15. Store Data with a Custom Document ID

Use doc() and set() when you want to specify the document ID.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 25,
});

The resulting structure is:

users
 |
 +-- user001
      |
      +-- name: John Doe
      +-- email: [email protected]
      +-- age: 25

16. Storing Different Data Types

Firestore documents can contain different types of values.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'age': 25,
  'isActive': true,
  'skills': ['Flutter', 'Dart', 'Firebase'],
  'address': {
    'city': 'Mumbai',
    'country': 'India',
  },
});

17. Store Date and Time

Firestore supports timestamp values.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'createdAt': Timestamp.now(),
});

You can also use a server-side timestamp:

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'createdAt': FieldValue.serverTimestamp(),
});

18. Store Application Data from a Form

A common Flutter pattern is collecting information from a form and storing it in Firestore.

Controllers

final nameController = TextEditingController();
final emailController = TextEditingController();

Save Function

Future saveUser() async {
  final name = nameController.text.trim();
  final email = emailController.text.trim();

  await FirebaseFirestore.instance
      .collection('users')
      .add({
    'name': name,
    'email': email,
    'createdAt': FieldValue.serverTimestamp(),
  });
}

Save Button

ElevatedButton(
  onPressed: saveUser,
  child: const Text('Save'),
)

19. Form Validation Before Storing Data

Always validate important user input before storing it.

Future saveUser() async {
  final name = nameController.text.trim();
  final email = emailController.text.trim();

  if (name.isEmpty || email.isEmpty) {
    return;
  }

  await FirebaseFirestore.instance
      .collection('users')
      .add({
    'name': name,
    'email': email,
  });
}

For larger forms, Flutter's Form and TextFormField widgets can be used for structured validation.


20. Retrieve a Single Document

Use get() to retrieve a document once.

final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  print(doc.data());
}

The retrieved document contains its fields and values.


21. Retrieve Document Data

final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  final data = doc.data();

  print(data?['name']);
  print(data?['email']);
  print(data?['age']);
}

22. Check Whether a Document Exists

final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

if (doc.exists) {
  print('Document found');
} else {
  print('Document not found');
}

23. Retrieve All Documents

Use get() on a collection reference to retrieve multiple documents.

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

for (final doc in snapshot.docs) {
  print('ID: ${doc.id}');
  print('Data: ${doc.data()}');
}

24. Retrieve Data as a List

Firestore query results can be converted into a list that can be displayed in Flutter.

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs.map((doc) {
  return {
    'id': doc.id,
    ...doc.data(),
  };
}).toList();

25. Display Stored Data in ListView

Firestore data can be displayed using Flutter's ListView.builder.

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs;

ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    final user = users[index].data();

    return ListTile(
      title: Text(user['name'] ?? ''),
      subtitle: Text(user['email'] ?? ''),
    );
  },
)

26. FutureBuilder for Retrieving Data

FutureBuilder is useful when retrieving Firestore data once and displaying the result in the Flutter UI.

FutureBuilder>>(
  future: FirebaseFirestore.instance
      .collection('users')
      .get(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return const Center(
        child: Text('Unable to load data'),
      );
    }

    if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
      return const Center(
        child: Text('No data found'),
      );
    }

    final users = snapshot.data!.docs;

    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        final user = users[index].data();

        return ListTile(
          title: Text(user['name'] ?? ''),
          subtitle: Text(user['email'] ?? ''),
        );
      },
    );
  },
)

27. Real-Time Data Retrieval

Cloud Firestore supports real-time listeners. Instead of retrieving data only once, an application can listen for changes and update the UI when the listened-to data changes.

FirebaseFirestore.instance
    .collection('users')
    .snapshots()
    .listen((snapshot) {
  for (final doc in snapshot.docs) {
    print(doc.data());
  }
});

28. StreamBuilder for Real-Time Data

StreamBuilder can automatically rebuild the Flutter UI when Firestore data changes.

StreamBuilder>>(
  stream: FirebaseFirestore.instance
      .collection('users')
      .snapshots(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return const Center(
        child: Text('Error loading data'),
      );
    }

    final users = snapshot.data?.docs ?? [];

    if (users.isEmpty) {
      return const Center(
        child: Text('No users found'),
      );
    }

    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        final user = users[index].data();

        return ListTile(
          title: Text(user['name'] ?? ''),
          subtitle: Text(user['email'] ?? ''),
        );
      },
    );
  },
)

29. Difference Between get() and snapshots()

Feature get() snapshots()
Purpose Retrieve data once Listen for data changes
Return Type Future Stream
Typical Widget FutureBuilder StreamBuilder
Real-Time Updates No continuous listener Yes
Common Use One-time data loading Live data screens

30. Retrieve Data Using Queries

Firestore queries allow the application to retrieve only documents that match specific conditions.

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .where(
      'isActive',
      isEqualTo: true,
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}

31. Retrieve Data by Email

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .where(
      'email',
      isEqualTo: '[email protected]',
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}

32. Retrieve Data by Category

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .where(
      'category',
      isEqualTo: 'Electronics',
    )
    .get();

33. Retrieve Data Using Multiple Conditions

Firestore supports compound queries when the selected query structure is supported.

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .where(
      'category',
      isEqualTo: 'Electronics',
    )
    .where(
      'isAvailable',
      isEqualTo: true,
    )
    .get();

34. Sort Retrieved Data

The orderBy() method can be used to specify the sort order.

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy('price')
    .get();

Descending Order

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy(
      'price',
      descending: true,
    )
    .get();

35. Limit Retrieved Data

The limit() method restricts the number of documents returned.

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .limit(10)
    .get();

Limiting data is useful when the application does not need to retrieve an entire collection at once.


36. Store and Retrieve Product Data

Store Product

await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .set({
  'name': 'Laptop',
  'price': 65000,
  'category': 'Electronics',
  'isAvailable': true,
});

Retrieve Product

final doc = await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .get();

if (doc.exists) {
  final product = doc.data();

  print(product?['name']);
  print(product?['price']);
}

37. Updating Stored Data

Application data often needs to be modified after it has been stored.

Use update() to modify selected fields.

await FirebaseFirestore.instance
    .collection('products')
    .doc('product001')
    .update({
  'price': 62000,
});

38. Update Multiple Fields

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .update({
  'name': 'John Smith',
  'age': 26,
  'isActive': true,
});

39. Delete Stored Data

Use delete() to remove a document.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .delete();

40. CRUD Operations

CRUD represents the four basic database operations:

Operation Firestore Method Purpose
Create add() / set() Store new data
Read get() / snapshots() Retrieve data
Update update() Modify existing data
Delete delete() Remove data

41. Storing User Data with Firebase Authentication

Cloud Firestore is commonly used with Firebase Authentication. Authentication identifies the user, while Firestore stores additional application-specific user information.

A common structure is:

users
 |
 +-- USER_UID
      |
      +-- name
      +-- email
      +-- phone
      +-- createdAt

Example

final user = FirebaseAuth.instance.currentUser;

if (user != null) {
  await FirebaseFirestore.instance
      .collection('users')
      .doc(user.uid)
      .set({
    'name': 'John Doe',
    'email': user.email,
    'createdAt': FieldValue.serverTimestamp(),
  });
}

42. Retrieve Current User Data

final user = FirebaseAuth.instance.currentUser;

if (user != null) {
  final doc = await FirebaseFirestore.instance
      .collection('users')
      .doc(user.uid)
      .get();

  if (doc.exists) {
    final data = doc.data();

    print(data?['name']);
    print(data?['email']);
  }
}

43. Store Nested Data

Firestore documents can contain nested maps.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'name': 'John Doe',
  'address': {
    'city': 'Mumbai',
    'state': 'Maharashtra',
    'country': 'India',
  },
});

44. Retrieve Nested Data

final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

final data = doc.data();

if (data != null) {
  final address = data['address'];

  print(address['city']);
  print(address['state']);
}

45. Store Data in Subcollections

Subcollections can be used when data belongs to a particular document.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .collection('orders')
    .doc('order001')
    .set({
  'product': 'Flutter Course',
  'price': 5000,
  'status': 'completed',
});

The structure becomes:

users
 |
 +-- user001
      |
      +-- orders
           |
           +-- order001
                |
                +-- product
                +-- price
                +-- status

46. Retrieve Subcollection Data

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .collection('orders')
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}

47. Convert Firestore Data into Dart Models

For small applications, working directly with maps may be sufficient. For larger applications, Dart model classes provide a cleaner and more maintainable approach.

User Model

class UserModel {
  final String id;
  final String name;
  final String email;

  UserModel({
    required this.id,
    required this.name,
    required this.email,
  });

  factory UserModel.fromFirestore(
    DocumentSnapshot> doc,
  ) {
    final data = doc.data() ?? {};

    return UserModel(
      id: doc.id,
      name: data['name'] ?? '',
      email: data['email'] ?? '',
    );
  }
}

48. Retrieve Data Using a Model

final snapshot = await FirebaseFirestore.instance
    .collection('users')
    .get();

final users = snapshot.docs
    .map(UserModel.fromFirestore)
    .toList();

for (final user in users) {
  print(user.name);
  print(user.email);
}

49. Store Data Using a Model

A model can also provide a method for converting Dart objects into Firestore-compatible maps.

class UserModel {
  final String id;
  final String name;
  final String email;

  UserModel({
    required this.id,
    required this.name,
    required this.email,
  });

  Map toFirestore() {
    return {
      'name': name,
      'email': email,
    };
  }
}

Save Model

final user = UserModel(
  id: 'user001',
  name: 'John Doe',
  email: '[email protected]',
);

await FirebaseFirestore.instance
    .collection('users')
    .doc(user.id)
    .set(user.toFirestore());

50. Firestore Service Class

Database operations can be moved into a separate service class.

class UserService {
  final FirebaseFirestore _db =
      FirebaseFirestore.instance;

  Future createUser({
    required String id,
    required String name,
    required String email,
  }) async {
    await _db.collection('users').doc(id).set({
      'name': name,
      'email': email,
      'createdAt': FieldValue.serverTimestamp(),
    });
  }

  Future>> getUser(
    String id,
  ) {
    return _db.collection('users').doc(id).get();
  }

  Future updateUser(
    String id,
    Map data,
  ) {
    return _db.collection('users').doc(id).update(data);
  }

  Future deleteUser(String id) {
    return _db.collection('users').doc(id).delete();
  }
}

51. Loading State

Database operations are asynchronous, so the UI should display a loading indicator while data is being retrieved.

bool isLoading = false;

Future loadUsers() async {
  setState(() {
    isLoading = true;
  });

  try {
    final snapshot = await FirebaseFirestore.instance
        .collection('users')
        .get();

    // Process data
  } finally {
    if (mounted) {
      setState(() {
        isLoading = false;
      });
    }
  }
}

52. Error Handling

Firestore operations can fail because of network problems, permission errors, invalid queries, or other conditions. Always handle possible errors.

try {
  final snapshot = await FirebaseFirestore.instance
      .collection('users')
      .get();

  print(snapshot.docs);
} on FirebaseException catch (e) {
  print('Firestore error: ${e.code}');
  print('Message: ${e.message}');
} catch (e) {
  print('Unexpected error: $e');
}

53. Empty State

An application should handle the case where no documents are returned.

if (snapshot.docs.isEmpty) {
  return const Center(
    child: Text('No data available'),
  );
}

Empty-state messages provide useful feedback instead of displaying a blank screen.


54. Complete Data State Flow

Request Data
     |
     v
   Loading
     |
     v
Firestore Request
     |
     +---- Error ----> Error State
     |
     +---- No Data --> Empty State
     |
     +---- Data ----> Success State
                         |
                         v
                    Display Data

55. Real-Time User List Example

class UsersScreen extends StatelessWidget {
  const UsersScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Users'),
      ),
      body: StreamBuilder<
          QuerySnapshot>>(
        stream: FirebaseFirestore.instance
            .collection('users')
            .snapshots(),
        builder: (context, snapshot) {
          if (snapshot.connectionState ==
              ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          if (snapshot.hasError) {
            return const Center(
              child: Text('Unable to load users'),
            );
          }

          final users = snapshot.data?.docs ?? [];

          if (users.isEmpty) {
            return const Center(
              child: Text('No users found'),
            );
          }

          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index].data();

              return ListTile(
                title: Text(user['name'] ?? ''),
                subtitle: Text(user['email'] ?? ''),
              );
            },
          );
        },
      ),
    );
  }
}

56. Storing Application Settings

Firestore can also store settings that need to be available across devices.

await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .set({
  'settings': {
    'darkMode': true,
    'notifications': true,
    'language': 'English',
  },
}, SetOptions(merge: true));

57. Retrieve Application Settings

final doc = await FirebaseFirestore.instance
    .collection('users')
    .doc('user001')
    .get();

final data = doc.data();

if (data != null) {
  final settings = data['settings'];

  print(settings['darkMode']);
  print(settings['notifications']);
  print(settings['language']);
}

58. Storing Orders

An e-commerce application can store order information in Firestore.

await FirebaseFirestore.instance
    .collection('orders')
    .add({
  'userId': 'user001',
  'productId': 'product001',
  'quantity': 2,
  'totalAmount': 130000,
  'status': 'pending',
  'createdAt': FieldValue.serverTimestamp(),
});

59. Retrieve Orders for a User

final snapshot = await FirebaseFirestore.instance
    .collection('orders')
    .where(
      'userId',
      isEqualTo: 'user001',
    )
    .get();

for (final doc in snapshot.docs) {
  print(doc.data());
}

60. Store Messages

Firestore can be used to store messages for chat or communication features.

await FirebaseFirestore.instance
    .collection('messages')
    .add({
  'senderId': 'user001',
  'message': 'Hello!',
  'createdAt': FieldValue.serverTimestamp(),
});

61. Retrieve Messages in Real Time

StreamBuilder<
    QuerySnapshot>>(
  stream: FirebaseFirestore.instance
      .collection('messages')
      .orderBy('createdAt')
      .snapshots(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) {
      return const CircularProgressIndicator();
    }

    final messages = snapshot.data!.docs;

    return ListView.builder(
      itemCount: messages.length,
      itemBuilder: (context, index) {
        final message = messages[index].data();

        return ListTile(
          title: Text(message['message'] ?? ''),
          subtitle: Text(message['senderId'] ?? ''),
        );
      },
    );
  },
)

62. Pagination

When a collection contains a large number of documents, retrieving everything at once may not be appropriate. Firestore supports query cursors that can be used to retrieve data in pages.

A typical pagination flow is:

First Request
     |
     v
Load First Page
     |
     v
Remember Last Document
     |
     v
Request Next Page
     |
     v
Append New Data
     |
     v
Continue Until No More Data

Pagination should be designed together with appropriate ordering and filtering.


63. Limit and Order Data for Pagination

final snapshot = await FirebaseFirestore.instance
    .collection('products')
    .orderBy('createdAt', descending: true)
    .limit(10)
    .get();

The last document from one query can be used as a cursor for a subsequent query when implementing cursor-based pagination.


64. Offline Data

Cloud Firestore provides offline capabilities on supported client platforms. Firestore can cache data that the application is actively using and synchronize local changes with the backend when connectivity returns.

This can help applications remain responsive when network connectivity is temporarily unavailable.

Firestore Offline Data Documentation


65. Firestore Security Rules

Application data should not be left publicly accessible in production. Firestore Security Rules can be used to control access to documents and collections.

For example, a user-specific document can be protected using the authenticated user's UID.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    match /users/{userId} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }
  }
}

This rule allows an authenticated user to access the user document whose document ID matches their Firebase Authentication UID.


66. Security and Data Validation

  • Use Firebase Authentication when user identity is required.
  • Protect Firestore data with Security Rules.
  • Do not store sensitive information unnecessarily.
  • Validate important data before storing it.
  • Use UID-based document structures for user-specific data.
  • Design queries that are compatible with your Security Rules.
  • Do not assume client-side validation is sufficient for database security.
  • Review Security Rules before deploying the application.

67. Firestore Data Architecture

A clean application architecture can separate the UI, business logic, and database operations.

Flutter UI
    |
    v
Controller / ViewModel
    |
    v
Repository / Service
    |
    v
Cloud Firestore
    |
    v
Firebase Backend

This approach makes the application easier to maintain and test.


68. Recommended Project Structure

lib/
├── main.dart
├── firebase_options.dart
├── models/
│   ├── user_model.dart
│   ├── product_model.dart
│   └── order_model.dart
├── services/
│   ├── firestore_service.dart
│   └── auth_service.dart
├── repositories/
│   └── user_repository.dart
├── screens/
│   ├── home_screen.dart
│   ├── users_screen.dart
│   ├── products_screen.dart
│   └── orders_screen.dart
└── widgets/
    ├── user_card.dart
    ├── product_card.dart
    └── loading_widget.dart

69. Common Mistakes

Mistake Better Approach
Loading all documents unnecessarily Use queries, limits, and pagination where appropriate.
Ignoring errors Handle Firestore exceptions.
Ignoring empty results Display an appropriate empty state.
No loading indicator Show loading feedback during asynchronous operations.
Public database rules in production Use appropriate Security Rules.
Mixing database code with every widget Use services or repositories.
Using raw maps everywhere Use Dart model classes for larger applications.
Retrieving data repeatedly Use appropriate state management and listeners.
Ignoring query limitations Design supported queries and indexes carefully.

70. Best Practices

  • Design your Firestore data structure before writing application code.
  • Use meaningful collection and document names.
  • Use model classes for complex applications.
  • Separate Firestore operations from UI widgets.
  • Handle loading, success, error, and empty states.
  • Use queries instead of downloading unnecessary data.
  • Use limit() and pagination for large datasets.
  • Use real-time listeners only where real-time updates are required.
  • Use server timestamps for server-generated timestamps when appropriate.
  • Protect data using Firebase Authentication and Security Rules.
  • Test Firestore Security Rules before production.
  • Keep sensitive information out of client-side code.
  • Use offline support thoughtfully when your application requires it.

71. Complete CRUD Example

Create

Future createUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .set({
    'name': 'John Doe',
    'email': '[email protected]',
    'createdAt': FieldValue.serverTimestamp(),
  });
}

Read

Future readUser() async {
  final doc = await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .get();

  print(doc.data());
}

Update

Future updateUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .update({
    'name': 'John Smith',
  });
}

Delete

Future deleteUser() async {
  await FirebaseFirestore.instance
      .collection('users')
      .doc('user001')
      .delete();
}

72. Mini Project: User Data Management App

Create a Flutter application that stores and retrieves user information using Cloud Firestore.

Features

  • Add a new user.
  • Display all users.
  • View individual user details.
  • Update user information.
  • Delete users.
  • Search or filter users.
  • Display loading state.
  • Display error state.
  • Display empty state.
  • Use Firebase Authentication for user identity.
  • Protect data with Firestore Security Rules.

Suggested Firestore Structure

users
 |
 +-- USER_UID
      |
      +-- name
      +-- email
      +-- phone
      +-- createdAt
      +-- updatedAt

73. Mini Project: Product Management App

Create a product management application that uses Firestore as its cloud database.

Product Structure

products
 |
 +-- product001
      |
      +-- name
      +-- price
      +-- category
      +-- description
      +-- isAvailable
      +-- createdAt

Required Operations

  1. Add product.
  2. Display products.
  3. Search or filter products.
  4. Sort products by price.
  5. Update product.
  6. Delete product.
  7. Display loading state.
  8. Handle errors.
  9. Handle empty results.

74. Interview Questions

  1. What is application data?
  2. Why do Flutter applications need persistent storage?
  3. What is Cloud Firestore?
  4. What is the Firestore data model?
  5. What is a collection?
  6. What is a document?
  7. What are Firestore fields?
  8. How do you add Cloud Firestore to a Flutter application?
  9. What is FirebaseFirestore.instance?
  10. What is the difference between add() and set()?
  11. How do you store data with a custom document ID?
  12. How do you retrieve a single document?
  13. How do you retrieve all documents from a collection?
  14. What is the difference between get() and snapshots()?
  15. How do you display Firestore data using FutureBuilder?
  16. How do you display real-time data using StreamBuilder?
  17. How do you filter Firestore data?
  18. What is where()?
  19. What is orderBy()?
  20. What is limit()?
  21. How do you update a Firestore document?
  22. How do you delete a Firestore document?
  23. How do Firebase Authentication and Firestore work together?
  24. What are Firestore Security Rules?
  25. Why should model classes be used in larger applications?
  26. What is a Firestore subcollection?
  27. What is real-time data retrieval?
  28. What is offline support in Firestore?

75. Quick Revision

Concept Firestore API
Firestore Instance FirebaseFirestore.instance
Collection collection()
Document doc()
Store with Auto ID add()
Store with Custom ID set()
Update Data update()
Retrieve Once get()
Real-Time Retrieval snapshots()
Filter Data where()
Sort Data orderBy()
Limit Data limit()
Delete Data delete()
Server Timestamp FieldValue.serverTimestamp()
Increment Value FieldValue.increment()
Array Add FieldValue.arrayUnion()
Array Remove FieldValue.arrayRemove()

76. Learning Outcomes

After completing this topic, you should be able to:

  • Explain application data and persistent storage.
  • Understand the Cloud Firestore data model.
  • Configure Firestore in a Flutter application.
  • Store data in Firestore.
  • Retrieve individual documents.
  • Retrieve multiple documents.
  • Update stored application data.
  • Delete application data.
  • Use Firestore queries.
  • Filter and sort retrieved data.
  • Display Firestore data using Flutter widgets.
  • Use FutureBuilder for one-time data retrieval.
  • Use StreamBuilder for real-time data.
  • Store nested data and subcollections.
  • Connect Firestore with Firebase Authentication.
  • Convert Firestore documents into Dart models.
  • Create Firestore service classes.
  • Handle loading, error, empty, and success states.
  • Apply Firestore Security Rules.
  • Understand pagination and offline data concepts.

77. Official Firebase Resources


78. JustAcademy Flutter Resources

For structured Flutter learning, practical development training, and additional Flutter topics, explore the following resources:


79. Summary

Storing and retrieving application data is a fundamental requirement for real-world Flutter applications. Cloud Firestore provides a flexible NoSQL database where application data can be organized into collections and documents.

Flutter applications can use Firestore to create, read, update, delete, query, and listen to application data in real time. One-time reads can be performed with get(), while real-time updates can be handled with snapshots(). Queries such as where(), orderBy(), and limit() help retrieve the required data efficiently.

For production applications, Firestore should be combined with proper data modeling, loading and error handling, Firebase Authentication where appropriate, Security Rules, and a clean service or repository architecture.

whatsapp